﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CoreHelper.Validation.Validators;

namespace CoreHelper.Validation
{
    static class ValidationUtils
    {
        public class RegexPatterns
        {
            public const string DateISO = @"^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$";
            public const string Digits = @"^\d+$";
            public const string Email = @"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-||_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+([a-z]+|\d|-|\.{0,1}|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])?([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$";
            public const string Mobile = @"^(1[34578][0-9])\d{8}$";
            public const string Number = @"^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$";
            public const string Phone = @"^(1[34578][0-9])\d{8}$";
            public const string QQ = @"^[1-9]\d{4,9}$";
            public const string Url = @"^(https?|s?ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$";
            public const string Chinese = @"^[\u4e00-\u9fa5]+$";
            public const string Password = @"^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&*_]+$)[a-zA-Z\d!@#$%^&*_]{6,20}$";
        }

        public class Messages
        {
            public const string Required = "这是必填字段";
            public const string Enum = "值不在枚举范围内";
            public const string Email = "请输入有效的电子邮件地址";
            public const string Url = "请输入有效的网址";
            public const string Date = "请输入有效的日期";
            public const string DateISO = "请输入有效的日期 (YYYY-MM-DD)";
            public const string Number = "请输入有效的数字";
            public const string Digits = "只能输入数字";
            public const string EqualTo = "你的输入不相同";
            public const string Maxlength = "最多可以输入 {0} 个字符";
            public const string Minlength = "最少要输入 {0} 个字符";
            public const string Rangelength = "请输入长度在 {0} 到 {1} 之间的字符串";
            public const string Range = "请输入范围在 {0} 到 {1} 之间的数值";
            public const string Max = "请输入不大于 {0} 的数值";
            public const string Min = "请输入不小于 {0} 的数值";
            public const string Regex = "输入格式不正确";
            public const string QQ = "qq号码格式错误";
            public const string Mobile = "手机号码格式错误";
            public const string Phone = "电话号码格式错误";
            public const string Chinese = "只能输入中文";
            public const string Custom = "输入的值不正确";
            public const string Password = "密码格式不正确";
        }

        static Dictionary<string, Func<RuleItem, BaseValidatorAttribute>> ValidationCreatorDict = new Dictionary<string, Func<RuleItem, BaseValidatorAttribute>>(StringComparer.OrdinalIgnoreCase)
            {
                {"DateISO",(item)=> new DateISOValidatorAttribute( item.Message) },
                {"Date",(item)=> new DateValidatorAttribute(item.Message) },
                {"Digits",(item)=>new DigitsValidatorAttribute(item.Message) },
                {"Email",(item)=>new EmailValidatorAttribute(item.Message) },
                {"EqualTo",(item)=>new EqualToValidatorAttribute(item.Params[0].ToString().TrimStart('#'), item.Message) },
                {"MaxLength",(item)=>new MaxLengthValidatorAttribute(Convert.ToInt32(item.Params[0]), item.Message) },
                {"Max",(item)=>new MaxValidatorAttribute(Convert.ToDouble(item.Params[0]), item.Message) },
                {"MinLength",(item)=>new MinLengthValidatorAttribute(Convert.ToInt32(item.Params[0]), item.Message) },
                {"Min",(item)=>new MinValidatorAttribute(Convert.ToDouble(item.Params[0]), item.Message) },
                {"Number",(item)=>new NumberValidatorAttribute(item.Message )},
                {"RangeLength",(item)=>new RangeLengthValidatorAttribute(Convert.ToInt32(item.Params[0]),Convert.ToInt32(item.Params[1]), item.Message) },
                {"Range",(item)=>new RangeValidatorAttribute(Convert.ToDouble(item.Params[0]),Convert.ToDouble(item.Params[1]), item.Message) },
                {"Regex",(item)=>new RegexValidatorAttribute(item.Params[0].ToString(),item.Message ) },
                {"Required",(item)=>new RequiredValidatorAttribute(item.Message) },
                {"Url",(item)=>new UrlValidatorAttribute(item.Message) },
                {"QQ",(item)=>new QQValidatorAttribute(item.Message) },
                {"Mobile",(item)=>new MobileValidatorAttribute(item.Message) },

                {"Phone",(item)=>new PhoneValidatorAttribute(item.Message) },
                {"Chinese",(item)=>new ChineseValidatorAttribute(item.Message) },
                {"Custom",(item)=>new CustomValidatorAttribute(item.Params[0].ToString(), item.Message) },
            };

        public static int? GetValueLenth(object value)
        {
            if (value == null) return null;

            if (value is string)
            {
                return ((string)value).Length;
            }
            else if (value is Array)
            {
                return ((Array)value).Length;
            }
            else if (value is ICollection)
            {
                return ((ICollection)value).Count;
            }
            else if (value is IEnumerable)
            {
                var len = 0;
                var enumerator = (value as IEnumerable).GetEnumerator();
                while (enumerator.MoveNext()) len++;
                enumerator.Reset();
                return len;
            }
            else
            {
                return null;
            }
        }

        public static double? GetValueSize(object value)
        {
            if (value == null) return null;
            if (value is int)
            {
                return (int)value;
            }
            if (value is byte?)
            {
                return (byte)value;
            }
            else if (value is float?)
            {
                return (float)value;
            }
            else if (value is double?)
            {
                return (double)value;
            }
            else if (value is decimal?)
            {
                return (double)(decimal)value;
            }
            else
            {
                double num;
                if (double.TryParse(value.ToString(), out num))
                {
                    return num;
                }
                return null;
            }
        }

        public static BaseValidatorAttribute NewValidation(string ruleName, RuleItem ruleItem)
        {
            Func<RuleItem, BaseValidatorAttribute> validationCreator;
            if (ValidationCreatorDict.TryGetValue(ruleName, out validationCreator))
            {
                return validationCreator(ruleItem);
            }
            else
            {
                return PassValidatorAttribute.Instance;
            }
        }

    }
}
